package authorize

import (
	"errors"
	"git.myscrm.cn/golang/common/uuid"
	"github.com/dgrijalva/jwt-go"
	"strings"
	"time"
)

// 云采购通用鉴权组件，服务间/应用间鉴权
var (
	EXPECTED_SCHEME = "bearer"
	ERROR_LACK_AUTHORIZATION = "请求头缺少authorization字段"
	TOKEN_INVALID = errors.New("couldn't handle this token")
)

type GetSignKeyFunc func(appid string) (signKey string)

// 服务间鉴权调用的payload
type ServiceAuthClaims struct {
	//appid应用标志
	A string `json:"a"`
	//扩展字段
	E string `json:"e"`
	jwt.StandardClaims
}

func JwtServiceAuthorize(authorizationStr string, getSignKeyFunc GetSignKeyFunc) error {
	splits := strings.SplitN(authorizationStr, " ", 2)
	if len(splits) < 2 {
		return errors.New(ERROR_LACK_AUTHORIZATION)
	}

	if !strings.EqualFold(splits[0], EXPECTED_SCHEME) {
		return errors.New("authorization格式有误")
	}
	accessToken := splits[1]

	// ===================== 测试生成jwt token start==================
	//testsignKey := "test666"
	//accessToken, err := GenerateServiceAuthToken("gfyx-java", "a=b;c=d", testsignKey)
	//if err != nil {
	//	return errors.New("accessToken生成失败")
	//}
	// ===================== 测试生成jwt token end=====================

	claims, err := GetServiceAuthTokenPayload(accessToken)
	if err != nil {
		return errors.New("payload解析失败"+err.Error())
	}

	appid := claims.A
	if appid == "" {
		return errors.New("应用标志不能为空")
	}
	signKey := getSignKeyFunc(appid)
	//signKey := stark.Configer.(stark.ApolloConfigerIface).GetStringValue("Auth."+appid+".AppKey", "")
	if signKey == "" {
		return errors.New("当前应用没有权限访问")
	}

	claims, err = ParseServiceAuthToken(accessToken, signKey)
	if err != nil {
		return errors.New("authorization验证失败"+err.Error())
	}
	if claims.A == "" {
		return errors.New("应用标志不能为空")
	}
	if claims.IssuedAt == 0 {
		return errors.New("签发时间不能为空")
	}
	issuedAt := time.Unix(claims.IssuedAt, 0)
	currentTime := time.Now()
	timeDiff := currentTime.Sub(issuedAt)
	hoursDiff := timeDiff.Hours()

	if hoursDiff > 2 {
		return errors.New("签名时间超过两个小时")
	}
	return nil
}

// 无秘钥获取payload
func GetServiceAuthTokenPayload(tokenString string) (*ServiceAuthClaims, error) {
	token, _ := jwt.ParseWithClaims(tokenString, &ServiceAuthClaims{}, func(token *jwt.Token) (interface{}, error) {
		return nil, nil
	})

	if token == nil {
		return nil, errors.New("payload解析失败")
	}
	claims, ok := token.Claims.(*ServiceAuthClaims)
	if ok {
		return claims, nil
	}

	return nil, TOKEN_INVALID
}


// 解析服务间鉴权token
func ParseServiceAuthToken(tokenString string, signingKey string) (*ServiceAuthClaims, error) {
	token, err := jwt.ParseWithClaims(tokenString, &ServiceAuthClaims{}, func(token *jwt.Token) (interface{}, error) {
		return []byte(signingKey), nil
	})

	if err != nil {
		return nil, err
	}

	if claims, ok := token.Claims.(*ServiceAuthClaims); ok && token.Valid {
		return claims, nil
	}

	return nil, TOKEN_INVALID
}


// 生成服务间调用jwt（测试用）
func GenerateServiceAuthToken(appId, extend string, signKey string) (string, error) {
	signingKey := []byte(signKey)
	claims := ServiceAuthClaims{
		appId,
		extend,
		jwt.StandardClaims{
			NotBefore: time.Now().Unix() - 10,
			IssuedAt:  time.Now().Unix(),
			ExpiresAt: time.Now().Unix() + 3600,
			Issuer:    "ycg-mycaigou",
			Id:        uuid.GetUUID(),
		},
	}
	token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)
	tokenString, err := token.SignedString(signingKey)
	return tokenString, err
}